package com.otaliastudios.cameraview.engine;

import android.content.Context;
import android.content.res.Configuration;
import android.graphics.ImageFormat;
import android.graphics.PointF;
import android.graphics.Rect;
import android.location.Location;
import android.os.Build;
import android.view.Surface;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.google.android.gms.tasks.Task;
import com.google.android.gms.tasks.Tasks;
import com.otaliastudios.cameraview.CameraOptions;
import com.otaliastudios.cameraview.LogPlus;
import com.otaliastudios.cameraview.PictureResult;
import com.otaliastudios.cameraview.VideoResult;
import com.otaliastudios.cameraview.controls.Facing;
import com.otaliastudios.cameraview.controls.Flash;
import com.otaliastudios.cameraview.controls.Hdr;
import com.otaliastudios.cameraview.controls.PictureFormat;
import com.otaliastudios.cameraview.controls.WhiteBalance;
import com.otaliastudios.cameraview.engine.offset.Axis;
import com.otaliastudios.cameraview.engine.offset.Reference;
import com.otaliastudios.cameraview.frame.ByteBufferFrameManager;
import com.otaliastudios.cameraview.frame.FrameManager;
import com.otaliastudios.cameraview.gesture.Gesture;
import com.otaliastudios.cameraview.internal.CropHelper;
import com.otaliastudios.cameraview.livedata.SizeLiveData;
import com.otaliastudios.cameraview.metering.MeteringRegions;
import com.otaliastudios.cameraview.picture.SnapshotGlPictureRecorder;
import com.otaliastudios.cameraview.preview.CameraPreview;
import com.otaliastudios.cameraview.preview.RendererCameraPreview;
import com.otaliastudios.cameraview.size.AspectRatio;
import com.otaliastudios.cameraview.size.Size;
import com.otaliastudios.cameraview.video.AviMatEncoder;
import com.otaliastudios.cameraview.video.SnapshotVideoRecorder;
import com.otaliastudios.cameraview.video.encoding.TextureMediaEncoder;

import org.opencv.android.OpenCVLoader;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class RtspEngine extends CameraBaseEngine implements ByteBufferFrameManager.BufferCallback {

    private final Context mContext;
    private Size videoSize;
    public List<Size> mSizes = new ArrayList<>();
    private RtspEngineListener mListener;
    private Surface surface;
    private AviMatEncoder aviMatEncoder;
    private Size fristSize;

    public Surface getSurface() {
        return surface;
    }

    public RtspEngine(Context context, @NonNull Callback callback) {
        super(callback);
        mContext = context;
    }


    @NonNull
    @Override
    protected List<Size> getPreviewStreamAvailableSizes() {
        Size size = fristSize == null ? SizeLiveData.Companion.get().getValue() : fristSize;
        if (screenOrientation()) {
            //竖屏
            size = size == null ? new Size(1920, 1080) : size;
        } else {
            //横屏
            size = size == null ? new Size(1080, 1920) : size.flip();
        }
        if (mSizes.isEmpty()) {
            mSizes.add(size);
        } else mSizes.set(0, size);
        LogPlus.w(size.toString());
        return mSizes;
    }

    @NonNull
    @Override
    protected List<Size> getFrameProcessingAvailableSizes() {
        return Collections.singletonList(mPreviewStreamSize);
    }

    public boolean screenOrientation() {
        return mContext.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
    }

    @Override
    protected void onPreviewStreamSizeChanged() {
        //restartPreview();
    }

    @Override
    protected boolean collectCameraInfo(@NonNull Facing facing) {
        return false;
    }


    @NonNull
    @Override
    protected Task<CameraOptions> onStartEngine() {
        if (mListener != null) mListener.onStartEngine();
        return Tasks.forResult(mCameraOptions);
    }

    @NonNull
    @Override
    protected Task<Void> onStartBind() {
        if (mListener != null)fristSize= mListener.onStartBind(mPreview);
        List<Size> sizeList = getPreviewStreamAvailableSizes();

        LogPlus.w("我来获取尺寸");
        mCaptureSize = sizeList.get(0);
        mPreviewStreamSize = sizeList.get(0);
        return Tasks.forResult(null);
    }

    @NonNull
    @Override
    protected Task<Void> onStartPreview() {
        getCallback().onCameraPreviewStreamSizeChanged();
        if (mListener != null) mListener.onStartPreview();
        Size previewSize = getPreviewStreamSize(Reference.VIEW);
        if (previewSize == null) {
            throw new IllegalStateException("previewStreamSize should not be null at this point.");
        }
        mPreview.setStreamSize(previewSize.getWidth(), previewSize.getHeight());
        mPreview.setDrawRotation(0);
        return Tasks.forResult(null);
    }

    @NonNull
    @Override
    protected Task<Void> onStopPreview() {
        LOG.i("onStopPreview:", "Started.");
        if (mVideoRecorder != null) {
            mVideoRecorder.stop(true);
            mVideoRecorder = null;
        }
        mPictureRecorder = null;
        getFrameManager().release();
        if (mListener != null) mListener.onStopPreview();
        return Tasks.forResult(null);
    }

    @NonNull
    @Override
    protected Task<Void> onStopEngine() {
        if (mListener != null) mListener.onStopEngine();
        return Tasks.forResult(null);
    }


    @NonNull
    @Override
    protected Task<Void> onStopBind() {
        return Tasks.forResult(null);
    }


    @NonNull
    @Override
    protected FrameManager instantiateFrameManager(int poolSize) {
        return new ByteBufferFrameManager(poolSize, this);
    }

    @Override
    protected void onTakePicture(@NonNull PictureResult.Stub stub, boolean doMetering) {

    }

    @Override
    protected void onTakePictureSnapshot(@NonNull PictureResult.Stub stub, @NonNull AspectRatio outputRatio, boolean doMetering) {
        LogPlus.w("onTakePictureSnapshot:", "executing.");
        // Not the real size: it will be cropped to match the view ratio
        Size outputSize = getUncroppedSnapshotSize(Reference.OUTPUT);
        Rect outputCrop = CropHelper.computeCrop(outputSize, outputRatio);
        int width = Math.max(outputCrop.height(), outputCrop.width());
        int height = Math.min(outputCrop.height(), outputCrop.width());
        outputSize = new Size(width, height);
        stub.size = outputSize;
        stub.rotation = 0;
        outputRatio = outputRatio.getX() > outputRatio.getY() ? outputRatio : outputRatio.flip();

        mPictureRecorder = new SnapshotGlPictureRecorder(stub, this,
                (RendererCameraPreview) mPreview, outputRatio, getOverlay());
        mPictureRecorder.take();
        LogPlus.w(outputSize.toString() + "--->" + outputRatio);
        LogPlus.w("onTakePictureSnapshot:", "executed.");
    }

    @Override
    protected void onTakeVideoSnapshot(@NonNull VideoResult.Stub stub, @NonNull AspectRatio outputRatio) {
        if (!(mPreview instanceof RendererCameraPreview)) {
            throw new IllegalStateException("Video snapshots are only supported with GL_SURFACE.");
        }
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {
            throw new IllegalStateException("Video snapshots are only supported on API 18+.");
        }
        RendererCameraPreview glPreview = (RendererCameraPreview) mPreview;
        Size outputSize = getUncroppedSnapshotSize(Reference.OUTPUT);
        if (outputSize == null) {
            throw new IllegalStateException("outputSize should not be null.");
        }
        Rect outputCrop = CropHelper.computeCrop(outputSize, outputRatio);
        int width = Math.max(outputCrop.height(), outputCrop.width());
        int height = Math.min(outputCrop.height(), outputCrop.width());
        outputSize = videoSize == null ? new Size(width, height) : videoSize;
        stub.size = outputSize;
        // Vertical:               0   (270-0-0)
        // Left (unlocked):        0   (270-90-270)
        // Right (unlocked):       0   (270-270-90)
        // Upside down (unlocked): 0   (270-180-180)
        // Left (locked):          270 (270-0-270)
        // Right (locked):         90  (270-0-90)
        // Upside down (locked):   180 (270-0-180)
        // The correct formula seems to be deviceOrientation+displayOffset,
        // which means offset(Reference.VIEW, Reference.OUTPUT, Axis.ABSOLUTE).
        int offset = getAngles().offset(Reference.VIEW, Reference.OUTPUT, Axis.ABSOLUTE);
        stub.rotation = 0;
        stub.videoFrameRate = Math.round(mPreviewFrameRate);
        LOG.i("onTakeVideoSnapshot", "rotation:", stub.rotation, "size:", stub.size);

        // Start.
        mVideoRecorder = new SnapshotVideoRecorder(this, glPreview, getOverlay());
        ((SnapshotVideoRecorder) mVideoRecorder).setSurfaceListener(surface -> {
            RtspEngine.this.surface = surface;
            if (stub.file.getAbsolutePath().endsWith(".avi")) {
                aviMatEncoder = new AviMatEncoder(surface, stub);
                aviMatEncoder.start();
            }
        });
        mVideoRecorder.start(stub);
    }

    @Override
    protected void onStopVideo() {
        if (aviMatEncoder != null) aviMatEncoder.stopAviRecord();
        super.onStopVideo();
    }

    @Override
    protected void onTakeVideo(@NonNull VideoResult.Stub stub) {

    }


    @Override
    public void setFrameProcessingFormat(int format) {
        mFrameProcessingFormat = ImageFormat.NV21;
    }

    @Override
    public void setZoom(float zoom, @Nullable PointF[] points, boolean notify) {

    }

    @Override
    public void setExposureCorrection(float EVvalue, @NonNull float[] bounds, @Nullable PointF[] points, boolean notify) {

    }

    @Override
    public void setFlash(@NonNull Flash flash) {

    }

    @Override
    public void setWhiteBalance(@NonNull WhiteBalance whiteBalance) {

    }

    @Override
    public void setHdr(@NonNull Hdr hdr) {

    }

    @Override
    public void setLocation(@Nullable Location location) {

    }

    @Override
    public void setPictureFormat(@NonNull PictureFormat pictureFormat) {

    }

    @Override
    public void setPreviewFrameRate(float previewFrameRate) {
        mPreviewFrameRate = previewFrameRate;
    }

    @Override
    public void setHasFrameProcessors(boolean hasFrameProcessors) {

    }

    @Override
    public void startAutoFocus(@Nullable Gesture gesture, @NonNull MeteringRegions regions, @NonNull PointF legacyPoint) {

    }

    @Override
    public void setPlaySounds(boolean playSounds) {

    }

    @Override
    public void onBufferAvailable(@NonNull byte[] buffer) {
        LogPlus.w("数据长度--->" + buffer.length);
    }

    public interface RtspEngineListener {
        void onStartEngine();

        Size onStartBind(CameraPreview preview);

        void onStartPreview();

        void onStopPreview();

        void onStopEngine();
    }

    public void setRtspEngineListener(RtspEngineListener listener) {
        mListener = listener;
    }

    public Size getVideoSize() {
        return videoSize;
    }

    public void setVideoSize(Size videoSize) {
        this.videoSize = videoSize;
    }
}
